package com.appengine.tnovoselec;

import java.io.File;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileWriteChannel;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

public class RefreshCache {

	private Map<String, String> conditions;
	private Map<String, String> backgrounds;
	private Map<String, City> cities;
	private List<String> locations;
	private List<Price> prices;

	private FileService fileService;

	public RefreshCache(FileService fileService) {
		super();
		this.fileService = fileService;
	}

	@SuppressWarnings("unused")
	public void doRefresh() {
		Logger log = Logger.getLogger(RefreshCache.class.getSimpleName());
		log.info("Refresh started!");
		parseBackgrounds();
		parseConditions();
		parseLocations();
		parseGasPrices();
		parseExchangeRate();
		HTTPRequest request;
		Document document = null;
		Element root = null;
		try {
			document = XMLUtil.getEmptyDoc();
			root = XMLUtil.createElement(document, "items", null);
		} catch (Exception e) {
			// TODO: handle exception
		}
		int i = 0;
		for (String city : locations) {
			try {
				if (city == null) {
					continue;
				}
				Element cityElement = XMLUtil.createElement(document, "item", root);
				XMLUtil.createElement(document, "id", String.valueOf(i), cityElement);
				XMLUtil.createElement(document, "lat", cities.get(city).getLat(), cityElement);
				XMLUtil.createElement(document, "lng", cities.get(city).getLng(), cityElement);
				XMLUtil.createElement(document, "title", cities.get(city).getName(), cityElement);
				XMLUtil.createElement(document, "url", Config.externalURL + escapeManual(city).toLowerCase().replace(" ", ""), cityElement);

				// request = new HTTPRequest(new URL(Config.apiUrl + "?weather="
				// + URLEncoder.encode(escapeManual(city).toLowerCase()) +
				// "&hl=" + "en-GB"));
				request = new HTTPRequest(new URL(Config.newApiUrl + URLEncoder.encode(escapeManual(city).toLowerCase())));

				URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
				HTTPResponse resp = fetcher.fetch(request);
				byte[] data = resp.getContent();
				String temp = new String(data, "UTF-8");
				Document doc = XMLUtil.getDoc(temp.getBytes());

				doc = parseXML(doc, city);
				Node imported = document.importNode(doc.getDocumentElement(), true);
				cityElement.appendChild(imported);

				GoogleWeatherServlet.CACHE.put(escapeManual(city).toLowerCase().replace(" ", ""), new DOMSource(doc));
				i++;
			} catch (Exception e) {
				log.info(e.getMessage());
				System.out.println(e.getMessage());
			}
		}
		document.appendChild(root);
		storeBlob(document);
		GoogleWeatherServlet.CACHE.put(Config.mainKey, new DOMSource(document));
	}

	private void parseConditions() {
		try {
			Document document = XMLUtil.getDoc(Util.getBytesFrom(new FileInputStream(new File(Config.conditionsXML)), true));
			conditions = new HashMap<String, String>();
			NodeList conditionTypes = document.getDocumentElement().getElementsByTagName("type");
			for (int i = 0; i < conditionTypes.getLength(); i++) {
				conditions.put(conditionTypes.item(i).getTextContent().toLowerCase(), XMLUtil.getNodeAttribute(conditionTypes.item(i), "url"));
			}
		} catch (Exception ex) {
			String s = ex.getMessage();
			System.out.println(s);
		}
	}

	private void parseBackgrounds() {
		try {
			Document document = XMLUtil.getDoc(Util.getBytesFrom(new FileInputStream(new File(Config.backgroundsXML)), true));
			backgrounds = new HashMap<String, String>();
			NodeList backgroundTypes = document.getDocumentElement().getElementsByTagName("type");
			for (int i = 0; i < backgroundTypes.getLength(); i++) {
				// conditions.add(conditionTypes.item(i).getTextContent());
				backgrounds.put(backgroundTypes.item(i).getTextContent().toLowerCase(), XMLUtil.getNodeAttribute(backgroundTypes.item(i), "url"));
			}
		} catch (Exception ex) {
			String s = ex.getMessage();
			System.out.println(s);
			// Log.getLog().logError(ex.getMessage(), ex);
			// lastExecutionErrorMessage = ex.getMessage();
		}
	}

	private Document parseXML(Document document, String location) {
		try {
			if (Util.isEmpty(conditions) || conditions.size() < 1) {
				// throw new
				// AdapterException("No conditions found! Aborting parser.");
			}

			// Document document = XMLUtil.getDoc(dataSource.getInputStream());
			Document doc = XMLUtil.getEmptyDoc();
			Node root = XMLUtil.createElement(doc, "weathers", null);

			Node currentCondition = document.getDocumentElement().getElementsByTagName("current_condition").item(0);
			int condition = 0;
			String backgroundUrl = "";
			String iconUrl = "";
			String windSpeed = "";
			String windDirection = "";
			String windUnit = "";
			for (Node n : XMLUtil.getChildNodes(currentCondition)) {
				if (n.getNodeName().equals("weatherDesc")) {
					XMLUtil.addAttirbute((Element) n, "data", n.getTextContent().trim());
					condition = 0;
					// backgroundUrl =
					// backgrounds.get(n.getTextContent().toLowerCase());
					// iconUrl =
					// conditions.get(n.getTextContent().toLowerCase());
				} else if (n.getNodeName().equals("temp_F")) {
					n.getParentNode().removeChild(n);
				} else if (n.getNodeName().equals("temp_C")) {
					XMLUtil.addAttirbute((Element) n, "data", n.getTextContent().trim());
				} else if (n.getNodeName().equals("humidity")) {
					XMLUtil.addAttirbute((Element) n, "data", n.getTextContent());
				} else if (n.getNodeName().equals("weatherIconUrl")) {
					n.getParentNode().removeChild(n);
				} else if (n.getNodeName().equals("windspeedKmph")) {
					windSpeed = n.getTextContent();
				} else if (n.getNodeName().equals("winddir16Point")) {
					windDirection = n.getTextContent();
				} else if (n.getNodeName().equals("weatherCode")) {
					XMLUtil.addAttirbute((Element) n, "data", n.getTextContent());
					iconUrl = conditions.get(n.getTextContent().toLowerCase());
					backgroundUrl = backgrounds.get(n.getTextContent().toLowerCase());
				}
			}

			Element ewDirection = XMLUtil.createElement(document, "wind_direction", (Element) currentCondition);
			XMLUtil.addAttirbute(ewDirection, "data", windDirection);
			currentCondition.appendChild(ewDirection);
			Element ewSpeed = XMLUtil.createElement(document, "wind_speed", (Element) currentCondition);
			XMLUtil.addAttirbute(ewSpeed, "data", windSpeed);
			XMLUtil.addAttirbute(ewSpeed, "unit", "km/h");
			currentCondition.appendChild(ewSpeed);
			Element backgroundUrlElement = XMLUtil.createElement(document, "background", (Element) currentCondition);
			Element iconEl = XMLUtil.createElement(document, "icon", (Element) currentCondition);
			XMLUtil.addAttirbute(iconEl, "data", iconUrl);
			XMLUtil.addAttirbute(backgroundUrlElement, "data", backgroundUrl);

			Node cond = doc.importNode(currentCondition, true);
			root.appendChild(cond);

			NodeList conds = document.getDocumentElement().getElementsByTagName("weather");
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			Calendar c = Calendar.getInstance();
			for (int i = 0; i < conds.getLength(); i++) {
				Node icon = null;
				for (Node n : XMLUtil.getChildNodes(conds.item(i))) {
					if (n.getNodeName().equals("weatherIconUrl")) {
						icon = n;
					} else if (n.getNodeName().equals("weatherDesc")) {
						XMLUtil.addAttirbute((Element) n, "data", n.getTextContent().trim());
						// Element iconE = XMLUtil.createElement(document,
						// "icon", (Element) conds.item(i));
						// XMLUtil.addAttirbute(iconE, "data",
						// conditions.get(n.getTextContent().toLowerCase()));
						// icon.getAttributes().getNamedItem("data")
						// .setTextContent(conditions.get(n.getAttributes().getNamedItem("data").getTextContent().toLowerCase()));
					} else if (n.getNodeName().equals("date")) {
						Element date = XMLUtil.createElement(document, "day_of_week", (Element) conds.item(i));
						String dateS = n.getTextContent();

						Date d = sdf.parse(dateS);

						c.setTime(d);
						XMLUtil.addAttirbute(date, "data", "" + c.get(Calendar.DAY_OF_WEEK));
					} else if (n.getNodeName().equals("tempMaxC")) {
						XMLUtil.addAttirbute((Element) n, "data", n.getTextContent());
					} else if (n.getNodeName().equals("tempMinC")) {
						XMLUtil.addAttirbute((Element) n, "data", n.getTextContent());
					} else if (n.getNodeName().equals("weatherCode")) {
						XMLUtil.addAttirbute((Element) n, "data", n.getTextContent());
						Element iconE = XMLUtil.createElement(document, "icon", (Element) conds.item(i));
						XMLUtil.addAttirbute(iconE, "data", conditions.get(n.getTextContent().toLowerCase()));
					}
				}
				Node fcond = doc.importNode(conds.item(i), true);
				root.appendChild(fcond);
			}

			doc.appendChild(root);
			return (doc);
		} catch (Exception ex) {
			String s = ex.getMessage();
			ex.printStackTrace();
			// Log.getLog().logError(ex.getMessage(), ex);
			// lastExecutionErrorMessage = ex.getMessage();
		}
		return null;
	}

	private void parseLocations() {
		locations = new ArrayList<String>();
		cities = new HashMap<String, City>();
		try {
			Document document = XMLUtil.getDoc(Util.getBytesFrom(new FileInputStream(new File(Config.locationsXML)), true));

			NodeList locs = document.getDocumentElement().getElementsByTagName("city");
			City city;
			for (int i = 0; i < locs.getLength(); i++) {
				city = new City();
				city.setName(locs.item(i).getTextContent());
				city.setLat(locs.item(i).getAttributes().getNamedItem("lat").getTextContent());
				city.setLng(locs.item(i).getAttributes().getNamedItem("lng").getTextContent());
				// conditions.add(conditionTypes.item(i).getTextContent());
				locations.add(locs.item(i).getTextContent().toLowerCase());
				cities.put(locs.item(i).getTextContent().toLowerCase(), city);
			}
		} catch (Exception ex) {
			String s = ex.getMessage();
			System.out.println(s);
			// Log.getLog().logError(ex.getMessage(), ex);
			// lastExecutionErrorMessage = ex.getMessage();
		}
	}

	private String escapeManual(String s) {
		return s.replaceAll("č", "c").replaceAll("ć", "c").replaceAll("ž", "z").replaceAll("š", "s").replaceAll("đ", "dj");
	}

	private void storeBlob(Document doc) {
		try {

			AppEngineFile file = fileService.createNewBlobFile("text/xml", "meteo.xml");
			FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);
			PrintWriter out = new PrintWriter(Channels.newWriter(writeChannel, "UTF8"));
			DOMSource domSource = new DOMSource(doc);
			StringWriter writer = new StringWriter();
			StreamResult result = new StreamResult(writer);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer = tf.newTransformer();
			transformer.transform(domSource, result);
			String content = writer.toString();
			out.print(content);
			out.close();
			writeChannel.closeFinally();
			BlobKey blobkey = fileService.getBlobKey(file);
			GoogleWeatherServlet.meteoKey = blobkey; // String path =
														// file.getFullPath();

			/*
			 * FileService fileService = FileServiceFactory.getFileService();
			 * GSFileOptionsBuilder optionsBuilder = new
			 * GSFileOptionsBuilder().setBucket(Config.BUCKETNAME)
			 * .setKey(Config
			 * .FILENAME).setMimeType("text/xml").setAcl("public_read");
			 * AppEngineFile writableFile =
			 * fileService.createNewGSFile(optionsBuilder.build()); boolean lock
			 * = true; FileWriteChannel writeChannel =
			 * fileService.openWriteChannel(writableFile, lock); PrintWriter out
			 * = new PrintWriter(Channels.newWriter(writeChannel, "UTF8"));
			 * DOMSource domSource = new DOMSource(doc); StringWriter writer =
			 * new StringWriter(); StreamResult result = new
			 * StreamResult(writer); TransformerFactory tf =
			 * TransformerFactory.newInstance(); Transformer transformer =
			 * tf.newTransformer(); transformer.transform(domSource, result);
			 * String content = writer.toString(); out.print(content);
			 * out.close(); writeChannel.closeFinally();
			 */
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void parseGasPrices() {
		try {
			prices = new ArrayList<Price>();
			HtmlCleaner cleaner = new HtmlCleaner();
			TagNode rootNode = cleaner.clean(new URL(Config.gasPricesURL));
			List<TagNode> divs = Util.getDivsByClass(Config.className, rootNode);
			TagNode divElement = (TagNode) divs.get(0);
			TagNode body[] = divElement.getElementsByName("tbody", true);
			TagNode tbody = body[0];
			for (TagNode node : tbody.getElementsByName("tr", true)) {
				String name = node.getElementsByName("td", true)[0].getText().toString();
				String price = node.getElementsByName("td", true)[1].getText().toString();
				Price priceObj = new Price(name, price);
				System.out.println(priceObj);
				prices.add(priceObj);
			}
			buildGasPricesXML();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	private void buildGasPricesXML() {
		try {
			Document doc = XMLUtil.getEmptyDoc();
			Element root = XMLUtil.createElement(doc, "root", null);
			for (Price price : prices) {
				Element p = XMLUtil.createElement(doc, "prices", root);
				XMLUtil.createElement(doc, "name", price.getName(), p);
				XMLUtil.createElement(doc, "price", price.getPrice(), p);
			}
			doc.appendChild(root);
			doc.normalize();

			AppEngineFile file = fileService.createNewBlobFile("text/xml", "gasPrices.xml");
			FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);
			PrintWriter out = new PrintWriter(Channels.newWriter(writeChannel, "UTF8"));
			DOMSource domSource = new DOMSource(doc);
			StringWriter writer = new StringWriter();
			StreamResult result = new StreamResult(writer);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer = tf.newTransformer();
			transformer.transform(domSource, result);
			String content = writer.toString();
			out.print(content);
			out.close();
			writeChannel.closeFinally();
			BlobKey blobkey = fileService.getBlobKey(file);
			GoogleWeatherServlet.gasPrices = blobkey;
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	private void parseExchangeRate() {
		try {
			HTTPRequest request = new HTTPRequest(new URL(Config.exchangeRateURL));
			URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
			HTTPResponse resp = fetcher.fetch(request);
			byte[] data = resp.getContent();
			String temp = new String(data, "UTF-8");
			Document doc = XMLUtil.getDoc(temp.getBytes());
			NodeList nodes = doc.getDocumentElement().getElementsByTagName("Currency");
			for (int i = 0; i < nodes.getLength(); i++) {
				Element node = (Element) nodes.item(i);
				String name = XMLUtil.getChildNodeValue(node, "Name");
				XMLUtil.createElement(doc, "image", Config.dropBoxPublicUrl + name + ".png", node);
			}
			doc.normalize();

			AppEngineFile file = fileService.createNewBlobFile("text/xml", "exchangeRates.xml");
			FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);
			PrintWriter out = new PrintWriter(Channels.newWriter(writeChannel, "UTF8"));
			DOMSource domSource = new DOMSource(doc);
			StringWriter writer = new StringWriter();
			StreamResult result = new StreamResult(writer);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer = tf.newTransformer();
			transformer.transform(domSource, result);
			String content = writer.toString();
			out.print(content);
			out.close();
			writeChannel.closeFinally();
			BlobKey blobkey = fileService.getBlobKey(file);
			GoogleWeatherServlet.exchangeRates = blobkey;
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public static void cleanBlobstore() {
		List<BlobInfo> blobsToDelete = new LinkedList<BlobInfo>();
		Iterator<BlobInfo> iterator = new BlobInfoFactory().queryBlobInfos();
		while (iterator.hasNext())
			blobsToDelete.add(iterator.next());
		
		blobsToDelete = blobsToDelete.subList(0, 1000);

		BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
		for (BlobInfo blobInfo : blobsToDelete)
			blobstoreService.delete(blobInfo.getBlobKey());
	}
}
